﻿using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

namespace Hsenl.Network {
    public class PacketReader {
        private int _bodySizeBits;
        private int _totalHeadBits;

        private readonly HBuffer _bufferRecv = new();
        private readonly ReaderWriterLockSlim _slim = new();

        public event Action<Memory<byte>> OnMessageReadedEvent; // 当读取出了包

        // 当抛出解析异常的时候, 不再抛出异常, 而是重置缓存区, 并 log 错误
        // tcp 的数据一定是可靠的, 所以在没bug 的情况下, tcp 是不会出现解析错误的
        // 但 udp 在极端情况下, 有可能出现数据不可靠, 此时就有可能让解析出错, 此时, 需要立即终止解析, 并重置缓冲区, 以防止后续的正确的数据被牵连.
        // 由于我们在发送 kcp 消息时, 保证了每个消息的完整性, 所以重置后, 不会影响后续的拆包
        // 该参数和 writer 中的 ensureMsgComplete 参数是对应的, 如果 ensureMsgComplete 为true, 那么这里也应该设置为 true. 反之亦然.
        private bool _parseExceptionReset;

        public int Position => this._bufferRecv.Position;
        public int Length => this._bufferRecv.Length;
        public int Origin => this._bufferRecv.Origin;

        public void Init(int bodySizeBits, int totalHeadBits, bool parseExceptionReset = false) {
            this._bodySizeBits = bodySizeBits;
            this._totalHeadBits = totalHeadBits;
            this._parseExceptionReset = parseExceptionReset;
        }

        public byte[] GetRecvBuffer(int length, out int offset, out int count) {
            this._slim.EnterReadLock();
            try {
                if (this._bufferRecv.Capacity < length)
                    this._bufferRecv.Capacity = length;

                offset = 0;
                count = length;
                var bytes = this._bufferRecv.GetBuffer();
                return bytes;
            }
            finally {
                this._slim.ExitReadLock();
            }
        }

        public Memory<byte> GetRecvBuffer(int length) {
            this._slim.EnterReadLock();
            try {
                // 我们用于接收的和用于做粘包操作的, 其实是同一块内存, 所以可以很大程度上, 减少一次数据 copy
                return this._bufferRecv.GetMemory(length);
            }
            finally {
                this._slim.ExitReadLock();
            }
        }

        // 读包, 拆包
        public void Read(Memory<byte> data) {
            this._slim.EnterReadLock();
            try {
                var len = data.Length;
                // 处理前, 先把 pos 推进, 这样, 上面 GetRecvBuffer 的时候, 再接收新数据就会连贯的放在pos后面
                this._bufferRecv.Position += len;
                CONTINUE:

                // 当前已经接收了多少数据, 包括这次的, 和之前剩的
                var dataSize = this._bufferRecv.Position;

                // 先尝试读消息头
                // 如果长度不足, 无法读取消息头, 那这次暂不处理, 继续接收
                if (dataSize <= this._totalHeadBits) {
                    goto RETURN;
                }

                // 尝试从消息头里, 读取消息体的大小
                var headSpan = this._bufferRecv.AsSpan(0, this._bodySizeBits);
                var bodySize = Unsafe.ReadUnaligned<ushort>(ref MemoryMarshal.GetReference(headSpan));
                if (bodySize <= 0) {
                    if (this._parseExceptionReset) {
                        this._bufferRecv.Reset();
                        Log.Error($"Invalid Head Message Size '{bodySize}'");
                    }
                    else {
                        throw new ParseMessageException($"Invalid Head Message Size '{bodySize}'");
                    }
                }

                // 到这里, 说明我们已经拿到了消息体的大小, 然后计算整个消息的大小, 看看长度够没够
                var messageSize = bodySize + this._totalHeadBits;
                // 当前接收的数据不是完整的消息, 还需继续接收
                if (dataSize < messageSize) {
                    goto RETURN;
                }

                // 如果到这里, 说明当前接受到的数据里, 是有完整消息的
                // 只提取消息体部分
                var bodyMemory = this._bufferRecv.AsMemory(this._totalHeadBits, bodySize);
                // 在数据流中跳过这个已经处理的消息, 并重新获取剩余的数据大小
                this._bufferRecv.Origin += messageSize;
                dataSize = this._bufferRecv.Position;
                // 读完了, 抛出事件
                this.OnRead(bodyMemory);

                // 说明还有剩余数据, 那就继续处理
                if (dataSize > 0) {
                    goto CONTINUE;
                }

                RETURN:
                if (this._bufferRecv.Origin != 0) {
                    if (dataSize != 0) {
                        // 把剩余的数据, 平移到 buffer 的开头, 防止buffer被无限的扩大
                        // 因为每次处理数据都是处理到不能处理为止, 所以这里的拷贝的数据始终都不大
                        var tmp = this._bufferRecv.AsSpan(0, dataSize);
                        this._bufferRecv.Origin = 0;
                        this._bufferRecv.Position = 0;
                        tmp.CopyTo(this._bufferRecv.AsSpan(0, dataSize));
                        this._bufferRecv.Position = dataSize;
                    }
                    else {
                        this._bufferRecv.Origin = 0;
                        this._bufferRecv.Position = 0;
                    }
                }
            }
            finally {
                this._slim.ExitReadLock();
            }
        }

        private void OnRead(Memory<byte> data) {
            try {
                this.OnMessageReadedEvent?.Invoke(data);
            }
            catch (ParseMessageException e) {
                if (this._parseExceptionReset) {
                    // 只要是解析数据出了问题, 我们都默认是数据拆包出错了, 直接停止当前, 需要及时的重置, 以防止后面正常的消息也被连累出错.
                    this._bufferRecv.Reset();
                    Log.Error(e);
                }
                else {
                    throw;
                }
            }
            catch (Exception exception) {
                Log.Error(exception);
            }
        }

        public void Reset() {
            this._slim.EnterReadLock();
            try {
                this._bufferRecv.Reset();
            }
            finally {
                this._slim.ExitReadLock();
            }
        }

        public void Dispose() {
            this.Reset();
            this._bodySizeBits = 0;
            this._totalHeadBits = 0;
            this.OnMessageReadedEvent = null;
        }
    }
}